/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Trade.h"
#include "MudPlayer.h"
#include "MudItem.h"
#include "Table.h"
#include "Session.h"
#include "Utils.h"
#include "StringBuilder.h"
#include "myassert.h"

using namespace Chronos;
using namespace std;

Trade::Trade(MudPlayer& player1_, MudPlayer& player2_)
    : player1(player1_)
    , player2(player2_)
    , player1Accepted(false)
    , player2Accepted(false)
    , player1Credits(0)
    , player2Credits(0)
{
    player1.setTrade(this);
    player2.setTrade(this);
    player1.write("You start a trade with " + player2.getName() + ".\n");
    player2.write(player1.getName() + " starts a trade with you.\n");
}


void Trade::cancel() {
    player1.write("trade cancelled.\n");
    player2.write("trade cancelled.\n");
    returnItems();
    closeTrade();
}


void Trade::accept(MudPlayer& player) {
    if (&player == &player1) {
        player1Accepted = true;
        if (!player2Accepted) {
            player1.write("OK\n");
            player2.write(player1.getName() + " has accepted the trade.\n");
        }
    } else {
        player2Accepted = true;
        if (!player1Accepted) {
            player2.write("OK\n");
            player1.write(player2.getName() + " has accepted the trade.\n");
        }
    }
    if (player1Accepted && player2Accepted) {
        player1.write("trade finished.\n");
        player2.write("trade finished.\n");
        tradeItems();
        closeTrade();
    }
}


MudItem* Trade::findItem(const std::string& name) {
    MudItem* item = player1Items.findItem(name);
    if (item != 0) return item;
    item = player2Items.findItem(name);
    return item;
}


void Trade::addItem(MudPlayer& player, MudItem& item) {
    player1Accepted = false;
    player2Accepted = false;
    if (&player == &player1) {
        player1Items.addItem(&item);
        player1.write("OK\n");
        player2.write(player1.getName() + " adds " + item.getName() + " to trade.\n");
    } else {
        player2Items.addItem(&item);
        player2.write("OK\n");
        player1.write(player2.getName() + " adds " + item.getName() + " to trade.\n");
    }
}


void Trade::addCredits(MudPlayer& player, unsigned int amount) {
    player1Accepted = false;
    player2Accepted = false;
    if (&player == &player1) {
        player1Credits += amount;
        player1.write("OK\n");
        StringBuilder buffer;
        buffer << player1.getName() << " adds " << amount << " credits to trade.\n";
        player2.write((const char*)buffer);
    } else {
        player2Credits += amount;
        player2.write("OK\n");
        StringBuilder buffer;
        buffer << player2.getName() << " adds " << amount << " credits to trade.\n";
        player1.write((const char*)buffer);
    }
}


void Trade::show(Session& session) const {
    Table table(3);
    table.addCell(player1.getName());
    table.addCell("<-->");
    table.addCell(player2.getName());

    if (player1Credits != 0 || player2Credits != 0) {
        if (player1Credits != 0)
            table.addCell((string)Utils::itoa(player1Credits) + " credits");
        else table.addCell("");
        table.addCell("");
        if (player2Credits != 0)
            table.addCell((string)Utils::itoa(player2Credits) + " credits");
        else table.addCell("");
    }

    const ItemContainer::Items items1 = player1Items.getItems();
    const ItemContainer::Items items2 = player2Items.getItems();
    ItemContainer::ItemsConstIter iter1 = items1.begin();
    ItemContainer::ItemsConstIter iter2 = items2.begin();
    while (iter1 != items1.end() || iter2 != items2.end()) {
        if (iter1 != items1.end()) {
            table.addCell((*iter1)->getName());
            iter1++;
        } else {
            table.addCell("");
        }
        table.addCell("");
        if (iter2 != items2.end()) {
            table.addCell((*iter2)->getName());
            iter2++;
        } else {
            table.addCell("");
        }
    }
    table.write(session.write());
    if (player1Accepted) {
        session.write() << player1.getName() << " has ACCEPTED\n";
    }
    if (player2Accepted) {
        session.write() << player2.getName() << " has ACCEPTED\n";
    }
}


void Trade::returnItems() {
    const ItemContainer::Items items1 = player1Items.getItems();
    for (ItemContainer::ItemsConstIter iter1 = items1.begin(); iter1 != items1.end(); ++iter1) {
        player1Items.removeItem(*iter1);
        player1.addItem(*iter1);
    }
    player1.addCredits(player1Credits);

    const ItemContainer::Items items2 = player2Items.getItems();
    for (ItemContainer::ItemsConstIter iter2 = items2.begin(); iter2 != items2.end(); ++iter2) {
        player2Items.removeItem(*iter2);
        player2.addItem(*iter2);
    }
    player2.addCredits(player2Credits);
}


void Trade::tradeItems() {
    const ItemContainer::Items items1 = player1Items.getItems();
    for (ItemContainer::ItemsConstIter iter1 = items1.begin(); iter1 != items1.end(); ++iter1) {
        player1Items.removeItem(*iter1);
        player2.write("You receive a " + (*iter1)->getName() + "\n");
        player2.addItem(*iter1);
    }
    if (player1Credits != 0) {
        player2.write((string)"You receive " + Utils::itoa(player1Credits) + " credits\n");
        player2.addCredits(player1Credits);
    }

    const ItemContainer::Items items2 = player2Items.getItems();
    for (ItemContainer::ItemsConstIter iter2 = items2.begin(); iter2 != items2.end(); ++iter2) {
        player2Items.removeItem(*iter2);
        player1.write("You receive a " + (*iter2)->getName() + "\n");
        player1.addItem(*iter2);
    }
    if (player2Credits != 0) {
        player1.write((string)"You receive " + Utils::itoa(player2Credits) + " credits\n");
        player1.addCredits(player2Credits);
    }
}


void Trade::closeTrade() {
    player1.setTrade(0);
    player2.setTrade(0);
    delete this;
}


